<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
    <meta charset="UTF-8">
    <title>猜拳小游戏</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"
            integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
            crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
            crossorigin="anonymous"></script>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
</head>
<body>
<div class="container" id="app">
    <div class="card border-light m-5">
        <div class="card-body">
            <div class="card-title">
                <h1 class="text-center">基于手势识别的猜拳小游戏</h1>
            </div>
            <div v-if="!recording" class="row justify-content-evenly w-50 mx-auto">
                <button type="button" class="col btn btn-lg btn-primary" @click="game_start()">开始游戏
                </button>
                <button type="button" class="col btn btn-lg btn-warning" v-if="captured"
                        @click="reset_image">清除照片
                </button>
            </div>
            <div v-else class="row justify-content-evenly w-50 mx-auto">
                <button type="button" class="col btn btn-lg btn-success" :class="{disabled: gesture_id===3}" @click="game_judge">确认出拳
                </button>
                <button type="button" class="col btn btn-lg btn-secondary" @click="game_end">结束游戏
                </button>
            </div>
            <div class="row justify-content-around mt-5">
                <div class="card col-md-5 shadow-lg">
                    <div class="card-body">
                        <div class="card-title">
                            <h2>玩家</h2>
                            <p v-if="get_gesture()">手势为{{ get_gesture() }}</p>
                        </div>
                        <div v-if="recording" class="card-text position-relative">
                            <video id="video" class="img-fluid position-absolute top-0 start-0"></video>
                            <canvas id="video_canvas" class="img-fluid position-absolute top-0 start-0"></canvas>
                        </div>
                        <img :src="gesture_img" v-else class="img-fluid" alt="我方手势">
                    </div>
                </div>
                <div class="card col-md-5 shadow-lg">
                    <div class="card-body">
                        <div class="card-title">
                            <h2>对手</h2>
                            <p v-if="get_opponent_gesture()">手势为{{ get_opponent_gesture() }}</p>
                        </div>
                        <img :src="get_opponent_img()" class="img-fluid" alt="对手手势">
                    </div>
                </div>
            </div>
            <div class="card my-3 mx-auto w-50" :class="{'text-bg-success': result==='win',
            'text-bg-danger': result==='lose', 'text-bg-warning': result==='draw'}" v-if="result !== null">
                <h3 class="card-title text-center my-3">{{ get_result() }}</h3>
                <p class="card-text text-center my-1">
                    我方手势为{{ get_gesture() }}，对手手势为{{ get_opponent_gesture() }}</p>
            </div>
        </div>
    </div>
</div>
<script>

    const id2gesture = ["rock", "paper", "scissors", "default_image"];
    const id2gesture_chs = ["石头", "布", "剪刀", "？？？"];
    let video_stream = null;
    let video_canvas = null;
    let video_element = null;
    let random_gesture_interval = null;
    let image_process_interval = null;

    function game_judge_post(gesture) {
        let result = null;
        $.ajax({
            type: "POST", // 数据提交类型
            url: "/api/judge", // 发送地址
            data: JSON.stringify({gesture: gesture}), //发送数据
            async: false, // 是否异步
            processData: false, //processData 默认为false，当设置为true的时候,jquery ajax 提交的时候不会序列化 data，而是直接使用data
            contentType: "application/json",
            success: function (res) {
                result = res;
            },
            error: function (e) {
                console.log("game judge post failed" + e);
            }
        });
        return result;
    }

    function image_process_post(form_data) {
        let result = null;
        $.ajax({
            type: "POST", // 数据提交类型
            url: "/api/image_process", // 发送地址
            data: form_data, //发送数据
            async: false, // 是否异步
            processData: false, //processData 默认为false，当设置为true的时候,jquery ajax 提交的时候不会序列化 data，而是直接使用data
            contentType: false,
            success: function (res) {
                console.log(res);
                result = res;
            },
            error: function (e) {
                console.log("image process post failed" + e);
            }
        });
        return result;
    }

    function capture_frame() {
        let canvas = document.createElement("canvas");
        let context = canvas.getContext('2d');

        video_element = document.getElementById("video");
        canvas.width = video_element.videoWidth;
        canvas.height = video_element.videoHeight;
        context.drawImage(video_element, 0, 0);

        return canvas.toDataURL("image/png");
    }

    function dataURLtoFile(dataurl, filename) {
        let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
            str = atob(arr[1]), n = str.length, u8arr = new Uint8Array(n);
        while (n--) {
            u8arr[n] = str.charCodeAt(n);
        }
        return new File([u8arr], filename, {type: mime});
    }

    function image_process() {
        let image = capture_frame();
        let file = dataURLtoFile(image, "image.png");
        let form_data = new FormData();
        form_data.append("image", file);
        return image_process_post(form_data);
    }

    const {createApp} = Vue;

    createApp({
        data() {
            return {
                result: null,
                gesture_id: 3,
                gesture_img: "/static/img/default_image.svg",
                opponent_gesture_id: 3,
                opponent_img_id: 3,
                recording: false,
                captured: false,
            }
        },

        methods: {
            game_start() {
                this.recording = true;
                this.result = null;
                this.opponent_gesture_id = 3;
                this.gesture_id = 3;
                navigator.mediaDevices.getUserMedia({video: true, audio: false}).then(
                    function (stream) {
                        video_stream = stream;
                        video_element = document.getElementById("video");
                        console.log(video_element);
                        video_element.srcObject = video_stream;
                        video_element.play();
                    }
                ).catch(
                    function (error) {
                        console.error("Error accessing camera: ", error);
                    }
                );
                if (random_gesture_interval !== null) {
                    clearInterval(random_gesture_interval);
                }
                random_gesture_interval = setInterval(() => {
                    if (this.opponent_gesture_id !== 3) {
                        this.opponent_img_id = this.opponent_gesture_id;
                        return;
                    }
                    this.opponent_img_id = (this.opponent_img_id + 1) % 3;
                }, 330);
                image_process_interval = setInterval(() => {
                    let image = capture_frame();
                    this.gesture_img = image;
                    let file = dataURLtoFile(image, "image.png");
                    let form_data = new FormData();
                    form_data.append("image", file);
                    let results = image_process_post(form_data);
                    console.log(results);
                    video_canvas = document.getElementById("video_canvas");
                    let context = video_canvas.getContext("2d");
                    context.clearRect(0, 0, video_canvas.width, video_canvas.height);
                    context.lineWidth = 5;
                    for (let result in results) {
                        if (result["score"] < 0.8) {
                            break;
                        }
                        let x1 = result["box_1"] / 320 * video_canvas.width;
                        let y1 = result["box_2"] / 320 * video_canvas.height;
                        let x2 = (result["box_3"] - x1) / 320 * video_canvas.width;
                        let y2 = (result["box_4"] - y1) / 320 * video_canvas.height;
                        context.strokeRect(x1, y1, x2, y2);
                    }
                    if (results[0]) {
                        this.gesture_id = results[0]["gesture"];
                    } else {
                        this.gesture_id = 3;
                    }
                }, 1000);
            },
            game_judge() {
                clearInterval(image_process_interval);
                this.recording = false;
                video_stream.getTracks().forEach(function (track) {
                    track.stop();
                });
                this.captured = true;
                let result = game_judge_post(this.gesture_id);
                if (result !== null) {
                    this.opponent_gesture_id = result["opponent"];
                    this.result = result["result"];
                }
            },
            game_end() {
                this.recording = false;
                this.result = null;
                video_stream.getTracks().forEach(function (track) {
                    track.stop();
                });
                this.captured = false;
                this.gesture_img = "/static/img/default_image.svg";
                clearInterval(image_process_interval);
                this.opponent_img_id = 3;
                clearInterval(random_gesture_interval);
            },
            reset_image() {
                this.captured = false;
                this.gesture_img = "/static/img/" + id2gesture[this.gesture_id] + ".svg";
            },
            get_opponent_gesture() {
                return id2gesture_chs[this.opponent_gesture_id];
            },
            get_gesture() {
                return id2gesture_chs[this.gesture_id];
            },
            get_opponent_img() {
                return "/static/img/" + id2gesture[this.opponent_img_id] + ".svg";
            },
            get_result() {
                if (this.result == null) {
                    return null;
                }
                if (this.result === "win") {
                    return "胜利";
                } else if (this.result === "lose") {
                    return "失败";
                } else {
                    return "平局";
                }
            }
        },

        created() {
            video_element = document.getElementById("video");
            video_canvas = document.getElementById("video_canvas");
        }

    }).mount('#app')
</script>
</body>
</html>